home *** CD-ROM | disk | FTP | other *** search
/ Mac Expert 1995 Winter / Mac Expert - Winter 95.iso / Les fichiers / Utilitaires divers / Divers / AWOL utilities ƒ / HoW Developer’s Kit 1.1ƒ / HoWSample / HoWSample.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-31  |  27.7 KB  |  1,092 lines  |  [TEXT/MPS ]

  1. /* HoWSample.c - Help on Wheels sample application */
  2.  
  3. /* to compile: BuildProgram HoWS */
  4.  
  5. /* - - - - - - - - - - - an AWOL Software Production - - - - - - - - - - - */
  6. /* - - - - - - - - - - - - - (c) 1994 Ross Brown - - - - - - - - - - - - - */
  7. /* - - - - - - - - -  Email:  ab026@freenet.carleton.ca  - - - - - - - - - */
  8.  
  9. /* includes */
  10.  
  11. #define    SystemSevenOrLater    1
  12. #include    <Aliases.h>
  13. #include    <AppleEvents.h>
  14. #include    <Balloons.h>
  15. #include    <Desk.h>
  16. #include    <Dialogs.h>
  17. #include    <Errors.h>
  18. #include    <Events.h>
  19. #include    <Files.h>
  20. #include    <Folders.h>
  21. #include    <Fonts.h>
  22. #include    <GestaltEqu.h>
  23. #include    <Icons.h>    /* new in System 7, extended beyond that released for MPW 3.2 */
  24. #include    <Language.h>
  25. #include    <Menus.h>
  26. #include    <OSEvents.h>
  27. #include    <OSUtils.h>
  28. #include    <QuickDraw.h>
  29. #include    <Resources.h>
  30. #include    <SegLoad.h>
  31. #include    <Script.h>
  32. #include    <Sound.h>
  33. #include    <SysEqu.h>
  34. #include    <ToolUtils.h>
  35. #include    <Traps.h>
  36. #include    <Types.h>
  37. #include    <Windows.h>
  38.  
  39. /* Change this to reflect the location of your copy of HoWLib.h. */
  40. #include    "::HoWLib.h"
  41.  
  42. #include    "HoWSample.h"
  43.  
  44. /* application preferences */
  45.  
  46. /* application globals */
  47.  
  48. #define    sleepTicks    15
  49. Boolean    done = false;
  50.  
  51. /* Where are the application file and the preferences file? */
  52. short    applicationFileRefNum;
  53. short    preferencesFileRefNum;
  54. short    preferencesFolderVRefNum;
  55. Boolean    preferencesFileUnwritable;
  56.  
  57. /* What is the help tag number of the current topic? */
  58. short    topicTag;
  59.  
  60. /* What is the Command-key equivalent of the Help key? */
  61. char    helpCommandChar;
  62.  
  63. /* How many System-defined items are there in the Help menu? */
  64. short    systemHelpItemCount;
  65.  
  66. /* System stuff */
  67.  
  68. SysEnvRec    mac;
  69.  
  70. /* as documented in Technical Note 304 */
  71. pascal    OSErr    SetDialogDefaultItem( DialogPtr theDialog, short newItem )
  72.     = { 0x303C, 0x0304, 0xAA68 };
  73. pascal    OSErr    SetDialogCancelItem( DialogPtr theDialog, short newItem )
  74.     = { 0x303C, 0x0305, 0xAA68 };
  75. pascal    OSErr    SetDialogTracksCursor( DialogPtr theDialog, Boolean tracks )
  76.     = { 0x303C, 0x0306, 0xAA68 };
  77. extern    pascal    Boolean    StdFilterProc( DialogPtr theDialog, EventRecord *theEvent, short *itemHit );
  78.  
  79. Boolean    wneIsImplemented;    /* Is _WaitNextEvent implemented? */
  80. Boolean    inForeground;        /* Is this application in the foreground under MultiFinder? */
  81.  
  82. Boolean    helpMgrPresent;
  83. Boolean    appleEventsPresent;
  84. Boolean    launchControl;
  85. Boolean    findFolderPresent;
  86. Boolean    aliasMgrPresent;
  87. Boolean    standardFile58;
  88. Boolean    hasFSSpecCalls;
  89. Boolean    helpServerPresent;
  90.  
  91. /* forward function declarations */
  92.  
  93. void    InfoAlert( const char *string );
  94. void    WarningAlert( const char *string );
  95. void    ErrorAlert( const char *string, long code );
  96.  
  97. char    *GetMessage( short messageID, char *message );
  98.  
  99. void    WarningMessage( short warningID );
  100. void    ErrorMessage( short errorID, long code );
  101.  
  102. char    *GetPreloadString( short stringID, char *string );
  103.  
  104. void    DoUpdate( WindowPtr window );
  105.  
  106. /* utilities */
  107.  
  108. #pragma    segment    Main
  109. void    DisplayHelp( short tag, Boolean casual )
  110. {
  111.     OSErr    osErr = HoWDisplay( tag, casual );
  112.     
  113.     if( !casual )
  114.     {
  115.         switch( osErr )
  116.         {
  117.         case noErr:
  118.         case userCanceledErr:
  119.             break;
  120.         case clientNotRegisteredErr:
  121.             ErrorMessage( iHelpNotAvailable, 0 );
  122.             break;
  123.         default:
  124.             ErrorMessage( iCantDisplayHelp, osErr );
  125.             break;
  126.         }
  127.     }
  128.     
  129.     UnloadSeg( (Ptr) HoWDisplay );
  130.     
  131. }
  132.  
  133. #pragma segment Initialize
  134. Boolean    TrapAvailable( short tNumber, TrapType tType )
  135. {
  136.  
  137.     /* Check and see if the trap exists. On 64K ROM machines, tType will be ignored. */
  138.     return NGetTrapAddress( tNumber, tType ) != GetTrapAddress( _Unimplemented );
  139.     
  140. }
  141.  
  142. #pragma    segment    Main
  143. void    AdjustMenus( Boolean option )
  144. {
  145. #pragma    unused( option )
  146.     
  147. }
  148.  
  149. #pragma    segment    Main
  150. Boolean    IsAppWindow( WindowPtr window )
  151. {
  152.     short    windowKind;
  153.  
  154.     if( window == nil )
  155.         return false;
  156.     else
  157.     {
  158.         /* application windows have windowKinds >= userKind (8) or dialogKind (2) */
  159.         windowKind = ( (WindowPeek) window )->windowKind;
  160.         return ( windowKind >= userKind ) || ( windowKind == dialogKind );
  161.     }
  162. }
  163.  
  164. #pragma    segment    Main
  165. Boolean    IsVolumeLocked( short vRefNum )
  166. {
  167.     HParamBlockRec    pb;
  168.  
  169.     pb.volumeParam.ioCompletion = nil;
  170.     pb.volumeParam.ioNamePtr = nil;
  171.     pb.volumeParam.ioVRefNum = vRefNum;
  172.     pb.volumeParam.ioVolIndex = 0;
  173.     
  174.     return PBHGetVInfoSync( &pb ) == noErr && ( pb.volumeParam.ioVAtrb & 0x8080 );
  175.     
  176. }
  177.  
  178. /* dialog utilities */
  179.  
  180. #pragma    segment    Dialogs
  181. short    GetDialogTopicTag( DialogPtr dialog, Point *mousePtr )
  182. {
  183.     GrafPtr    savePort;
  184.     short    item;
  185.     short    dialogTopicTag = topicTag;
  186.     
  187.     GetPort( &savePort );
  188.     SetPort( dialog );
  189.     GlobalToLocal( mousePtr );
  190.     SetPort( savePort );
  191.     
  192.     /* You should set topicTag to a base value before calling ModalDialog or any other function */
  193.     /* which supports a modal dialog filter, such as CustomGetFile or CustomPutFile. */
  194.     /* Define a tag in your help document with that value which describes the dialog generally. */
  195.     /* For each interesting item in the dialog, define a tag with value equal to the base value */
  196.     /* plus the dialog item number.  GeneralModalDialogFilter will then handle both casual help */
  197.     /* displays (by clicking) and non-casual help displays (by pressing help or Command-Period) */
  198.     /* on a per-item basis, with the help of this function. */
  199.     item = 1 + findditem( dialog, mousePtr );
  200.     if( item > 0 )
  201.         dialogTopicTag += item;
  202.         
  203.     return dialogTopicTag;
  204.         
  205. }
  206.  
  207. #pragma    segment    Dialogs
  208. pascal    Boolean    GeneralModalDialogFilter( DialogPtr dialog, EventRecord *theEvent, short *itemHit )
  209. {
  210.  
  211.     switch( theEvent->what )
  212.     {
  213.     
  214.     case updateEvt:
  215.         if( (DialogPtr) theEvent->message != dialog )
  216.         {
  217.             if( IsDialogEvent( theEvent ) )
  218.             {    /* Handle update event in dialog behind this one. */
  219.                 DialogPtr    otherDialog;
  220.                 short    otherItemHit;
  221.                 return DialogSelect( theEvent, &otherDialog, &otherItemHit );
  222.             }
  223.             else
  224.             {    /* Handle update event in window behind this one. */
  225.                 DoUpdate( (WindowPtr) theEvent->message );
  226.                 return true;
  227.             }
  228.         }
  229.         break;
  230.         
  231.     case keyDown:
  232.         {
  233.             unsigned    char    characterCode = theEvent->message & charCodeMask;
  234.             if( characterCode == helpCommandChar && theEvent->modifiers & cmdKey )
  235.                 characterCode = 0x05;
  236.             switch( characterCode )
  237.             {
  238.             case 0x05:    /* Help */
  239.                 {
  240.                     Point    mouse = theEvent->where;
  241.                     short    dialogTopicTag = GetDialogTopicTag( dialog, &mouse );
  242.                     WindowPeek    windowPeek;
  243.                     DialogPtr    hiddenDialogs[ 16 ];
  244.                     DialogPtr    *hiddenDialog = hiddenDialogs;
  245.                     /* Hide all visible modal dialogs before displaying help, so that */
  246.                     /* switch can occur. */
  247.                     for( windowPeek = (WindowPeek) dialog; windowPeek != nil && windowPeek->windowKind == dialogKind && windowPeek->spareFlag < 0; windowPeek = windowPeek->nextWindow )
  248.                     {
  249.                         if( windowPeek->visible )
  250.                         {
  251.                             DialogPtr    dialogToHide = (DialogPtr) windowPeek;
  252.                             ShowHide( dialogToHide, false );
  253.                             *hiddenDialog++ = dialogToHide;
  254.                         }
  255.                     }
  256.                     /* This is a non-casual help display.  The user directly asked for help. */
  257.                     DisplayHelp( dialogTopicTag, false );
  258.                     while( hiddenDialog > hiddenDialogs )
  259.                         ShowHide( *--hiddenDialog, true );
  260.                 }
  261.                 *itemHit = 0;
  262.                 return true;
  263.             }
  264.         }
  265.         break;
  266.         
  267.     case mouseDown:
  268.         {
  269.             Point    mouse = theEvent->where;
  270.             short    dialogTopicTag = GetDialogTopicTag( dialog, &mouse );
  271.             /* This is an example of a casual help display. */
  272.             /* A casual display is effective only if the server is started and running in */
  273.             /* the background, with the "follow" option in effect, and already displaying */
  274.             /* this client's help file. */
  275.             DisplayHelp( dialogTopicTag, true );
  276.         }
  277.         break;
  278.         
  279.     }
  280.     
  281.     {    /* Let the standard filter procedure handle it. */
  282.         GrafPtr    savePort;
  283.         Boolean    handled;
  284.         GetPort( &savePort );
  285.         SetPort( dialog );
  286.         handled = StdFilterProc( dialog, theEvent, itemHit );
  287.         SetPort( savePort );
  288.         return handled;
  289.     }
  290.     
  291. }
  292.  
  293. /* dialogs */
  294.  
  295. #pragma    segment    DoNothing
  296. void    DoNothingDialog( void )
  297. {
  298.     DialogRecord    doNothingDialogRecord;
  299.     DialogPtr    doNothingDialog = GetNewDialog( rDoNothingDialog, &doNothingDialogRecord, (WindowPtr) -1 );
  300.     short    itemHit;
  301.     
  302.     SetCursor( &qd.arrow );
  303.  
  304.     SetDialogDefaultItem( doNothingDialog, ok );
  305.     SetDialogCancelItem( doNothingDialog, 0 );
  306.     SetDialogTracksCursor( doNothingDialog, false );
  307.     
  308.     ShowWindow( doNothingDialog );
  309.     do
  310.     {
  311.         topicTag = rDoNothingDialog * cTagSpacing;
  312.         ModalDialog( (ModalFilterProcPtr) GeneralModalDialogFilter, &itemHit );
  313.         switch( itemHit )
  314.         {
  315.         case iDNDSqueak:
  316.             SysBeep( 60 );
  317.             break;
  318.         }
  319.     }
  320.     while( itemHit != iDNDSqueak );
  321.     
  322.     CloseDialog( doNothingDialog );
  323.     DisposeHandle( doNothingDialogRecord.items );
  324.     
  325. }
  326.  
  327. /* Apple event handlers */
  328.  
  329. #pragma    segment    Main
  330. pascal    void    HelpEventHandler( short id )
  331. {
  332.     
  333.     /* This is a sample of what your application can do in response to a Help event. */
  334.     /* Help events are triggered by the user clicking a "hot" hypertext button in the help file. */
  335.     /* The value in "id" is a positive integer.  For example, if the user clicks in a part */
  336.     /* of the help file tagged "-23456", and there is no "23456" tag anywhere in the help file, */
  337.     /* this function will be called with id = 23456.  If there is a "23456" tag, then the button */
  338.     /* is a "link" button, and no Help event will occur. */
  339.     
  340.     switch( id )
  341.     {
  342.     case jokeTag:
  343.         {    /* Play the joke sound and return to the help server. */
  344.             Handle    soundHandle;
  345.             short    saveRefNum = CurResFile( );
  346.             UseResFile( applicationFileRefNum );
  347.             soundHandle = Get1Resource( soundListRsrc, rJokeSound );
  348.             UseResFile( saveRefNum );
  349.             if( soundHandle != nil )
  350.             {
  351.                 SndPlay( nil, soundHandle, false );
  352.                 ReleaseResource( soundHandle );
  353.             }
  354.             DisplayHelp( 0, false );
  355.         }
  356.         break;
  357.     default:
  358.         /* This is some unknown help event. */
  359.         ErrorAlert( "HELP EVENT!", id );
  360.         break;
  361.     }
  362.     
  363. }
  364.  
  365. #pragma    segment    Main
  366. pascal    OSErr    AppleEventHandler( AppleEvent *appleEventPtr, AppleEvent *replyEventPtr, long refCon )
  367. {
  368. #pragma    unused( replyEventPtr, refCon )
  369.     OSErr    osErr = noErr;
  370.     DescType    descType;
  371.     Size    size;
  372.     AEKeyword    keyword;
  373.  
  374.     if( osErr == noErr )
  375.     {    /* Figure out which kind of event this is. */
  376.         osErr = AEGetAttributePtr( appleEventPtr, keyEventIDAttr, typeWildCard, &descType, (Ptr) &keyword, sizeof( keyword ), &size );
  377.     }
  378.     
  379.     if( osErr == noErr )
  380.     {
  381.         /* Your application has to support the Required suite of Apple events, because its */
  382.         /* 'SIZE' resource tells Finder that it is high-level-event-aware.  See Inside Macintosh */
  383.         /* Volume VI for information on handling these events. */
  384.         switch( keyword )
  385.         {
  386.         case kAEOpenApplication:
  387.             break;
  388.         case kAEOpenDocuments:
  389.         case kAEPrintDocuments:
  390.             break;
  391.         case kAEQuitApplication:
  392.             /* We can't call Terminate here.  Let the main event loop do the terminating. */
  393.             done = true;
  394.             break;
  395.         default:
  396.             osErr = errAEEventNotHandled;
  397.             break;
  398.         }
  399.     }
  400.  
  401.     return osErr;
  402.     
  403. }
  404.  
  405. /* initialize & terminate */
  406.  
  407. #pragma    segment    Initialize
  408. Boolean    Initialize( )
  409. {
  410.     
  411.     /* Initialize mac. */
  412.     (void) SysEnvirons( curSysEnvVers, &mac );
  413.     
  414.     {    /* Gestalt... */
  415.         long    response;
  416.         helpMgrPresent = ( Gestalt( gestaltHelpMgrAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltHelpMgrPresent );
  417.         appleEventsPresent = ( Gestalt( gestaltAppleEventsAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltAppleEventsPresent );
  418.         launchControl = ( Gestalt( gestaltOSAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltLaunchControl );
  419.         findFolderPresent = ( Gestalt( gestaltFindFolderAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltFindFolderPresent );
  420.         aliasMgrPresent = ( Gestalt( gestaltAliasMgrAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltAliasMgrPresent );
  421.         standardFile58 = ( Gestalt( gestaltStandardFileAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltStandardFile58 );
  422.         hasFSSpecCalls = ( Gestalt( gestaltFSAttr, &response ) == noErr ) && BitTst( &response, 31 - gestaltHasFSSpecCalls );
  423.     }
  424.     
  425.     /* Initialize wneIsImplemented. */
  426.     wneIsImplemented = mac.machineType >= 0 && TrapAvailable( _WaitNextEvent, ToolTrap );
  427.     
  428.     /* Initialize inForeground. */
  429.     inForeground = true;
  430.     
  431.     /* Initialize the relevant parts of System. */
  432.     InitGraf( (Ptr) &qd.thePort );
  433.     InitFonts( );
  434.     InitWindows( );
  435.     InitMenus( );
  436.     InitDialogs( nil );
  437.     InitCursor( );
  438.     
  439.     if( !appleEventsPresent )
  440.     {
  441.         ErrorMessage( iHoWSampleRequiresAppleEvents, 0 );
  442.         return false;
  443.     }
  444.     
  445.     if( !launchControl )
  446.     {
  447.         ErrorMessage( iHoWSampleRequiresProcessManager, 0 );
  448.         return false;
  449.     }
  450.     
  451.     if( !findFolderPresent )
  452.     {
  453.         ErrorMessage( iHoWSampleRequiresFolderManager, 0 );
  454.         return false;
  455.     }
  456.     
  457.     if( !aliasMgrPresent )
  458.     {
  459.         ErrorMessage( iHoWSampleRequiresAliasManager, 0 );
  460.         return false;
  461.     }
  462.     
  463.     if( !standardFile58 )
  464.     {
  465.         ErrorMessage( iHoWSampleRequiresEnhancedStandardFile, 0 );
  466.         return false;
  467.     }
  468.     
  469.     if( !hasFSSpecCalls )
  470.     {
  471.         ErrorMessage( iHoWSampleRequiresFSSpecCalls, 0 );
  472.         return false;
  473.     }
  474.     
  475.     if( !wneIsImplemented )
  476.     {
  477.         ErrorMessage( iHoWSampleRequiresWaitNextEvent, 0 );
  478.         return false;
  479.     }
  480.     
  481.     {    /* Set up the menu bar. */
  482.         SetMenuBar( GetNewMBar( rMenuBar ) );
  483.         AddResMenu( GetMHandle( mApple ), 'DRVR' );
  484.         if( helpMgrPresent )
  485.         {    /* If this isn't called before DrawMenuBar, it will fail later if SpeedyFinder7's Help menu removal option is on. */
  486.             MenuHandle    dummyMenuHandle;
  487.             (void) HMGetHelpMenuHandle( &dummyMenuHandle );
  488.         }
  489.         DrawMenuBar( );
  490.     }
  491.     
  492.     {    /* Open the preferences file. */
  493.         OSErr    osErr;
  494.         long    preferencesFolderDirID;
  495.         char    preferencesFileName[ 256 ];
  496.         FSSpec    fsSpec;
  497.         const    Boolean    resolveAliasChains = true;
  498.         Boolean    targetIsFolder;
  499.         Boolean    wasAliased;
  500.         osErr = FindFolder( kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &preferencesFolderVRefNum, &preferencesFolderDirID );
  501.         if( osErr != noErr )
  502.         {
  503.             ErrorMessage( iCantFindPreferencesFolder, osErr );
  504.             return false;
  505.         }
  506.         osErr = FSMakeFSSpec( preferencesFolderVRefNum, preferencesFolderDirID, (Str255) c2pstr( GetPreloadString( iHoWSamplePreferencesFileName, preferencesFileName ) ), &fsSpec );
  507.         if( osErr != noErr && osErr != fnfErr )
  508.         {
  509.             ErrorMessage( iCantAccessPreferencesFile, osErr );
  510.             return false;
  511.         }
  512.         osErr = ResolveAliasFile( &fsSpec, resolveAliasChains, &targetIsFolder, &wasAliased );
  513.         if( ( osErr != noErr && osErr != fnfErr ) || targetIsFolder )
  514.         {
  515.             ErrorMessage( iCantResolvePreferencesFileAlias, osErr );
  516.             return false;
  517.         }
  518.         osErr = FSpCreate( &fsSpec, tHoWSample, tHoWSamplePreferences, smSystemScript );
  519.         if( osErr != noErr && osErr != dupFNErr && osErr != wPrErr )
  520.         {
  521.             ErrorMessage( iCantCreatePreferencesFile, osErr );
  522.             return false;
  523.         }
  524.         FSpCreateResFile( &fsSpec, tHoWSample, tHoWSamplePreferences, smSystemScript );
  525.         applicationFileRefNum = CurResFile( );
  526.         /* It is necessary to check volume lock status because the file system will let you */
  527.         /* open a file for write as long as the file itself is not locked. */
  528.         if( preferencesFileUnwritable = IsVolumeLocked( preferencesFolderVRefNum ) || ( preferencesFileRefNum = FSpOpenResFile( &fsSpec, fsRdPerm | fsWrPerm ) ) == -1 )
  529.         {
  530.             if( ( preferencesFileRefNum = FSpOpenResFile( &fsSpec, fsRdPerm ) ) == -1 )
  531.                 WarningMessage( iPreferencesFileUnreadable );
  532.             else
  533.                 WarningMessage( iPreferencesFileUnwritable );
  534.         }
  535.         UseResFile( applicationFileRefNum );
  536.     }
  537.     
  538.     {    /* Register with Help on Wheels for help service using help file alias resource */
  539.         /* in preferences or application file.  The alias resource comes from the */
  540.         /* “HoWSample Help alias” Finder alias file, so it contains relative alias information. */
  541.         /* This information is relative to the Finder alias file itself, which was created in */
  542.         /* the same directory as the help file.  HoWFindHelpFile expects the alias resource */
  543.         /* to be relative to the application file. */
  544.         /* So, as long as the help file and the application file are in the same directory, */
  545.         /* this information can be used to resolve the alias with a fast alias search. */
  546.         /* If the fast search fails, HoWFindHelpFile will do an exhaustive search of all */
  547.         /* volumes, but if the exhaustive search fails once, the preferences file's alias */
  548.         /* resource is marked to prevent repeating the failed search. */
  549.         /* Once successfully resolved, the non-relative information in the alias record */
  550.         /* (volume, file ID) will keep track of the help file. */
  551.         FSSpec    helpFileFSSpec;
  552.         OSErr    osErr = HoWFindHelpFile( preferencesFileRefNum, applicationFileRefNum, rHelpFileAlias, &helpFileFSSpec );
  553.         helpServerPresent = false;
  554.         if( osErr == noErr )
  555.         {    /* We don't start the server right away.  The server will start when the user */
  556.             /* asks for help.  If we did ask to start the server right away, HoWRegister */
  557.             /* would return noServerApplErr if the help server application cannot be found. */
  558.             /* noServerApplErr is not fatal; the client is registered anyway, and a non-casual */
  559.             /* HoWDisplay call will cause the client engine to offer the user a TeachText */
  560.             /* version of the help file.  The same thing could happen to us when we call */
  561.             /* HoWDisplay, but we can't tell that it's happening.  The 'Lang' resource gives */
  562.             /* the language code for which this application has been localized.  By preference, */
  563.             /* if it can find support in the help file, the help server will use the current */
  564.             /* script's current language, or its own language, before using this default language. */
  565.             LangCode    **clientLanguageHandle = (LangCode **) GetIndResource( 'Lang', 1 );
  566.             LangCode    clientLanguage = ( clientLanguageHandle != nil ) ? **clientLanguageHandle : langEnglish;
  567.             osErr = HoWRegister( &helpFileFSSpec, helpFileVersion, clientLanguage, false, (HoWHandlerProcPtr) HelpEventHandler );
  568.             switch( osErr )
  569.             {
  570.             case noErr:
  571.                 helpServerPresent = true;
  572.                 break;
  573.             case wrongVersionErr:
  574.                 WarningMessage( iHelpFileWrongVersion );
  575.                 break;
  576.             default:
  577.                 WarningMessage( iHoWRegisterFailed );
  578.                 break;
  579.             }
  580.             UnloadSeg( (Ptr) HoWRegister );
  581.         }
  582.         /* If osErr != noErr, we don't complain.  For example, a return code of fnfErr from */
  583.         /* HoWFindHelpFile may just mean that the user has trashed the help file. */
  584.     }
  585.     
  586.     if( helpMgrPresent )
  587.     {    /* Add the "HoWSample Help" item and subsidiary items to the Help menu. */
  588.         MenuHandle    helpMenuHandle;
  589.         OSErr    osErr = HMGetHelpMenuHandle( &helpMenuHandle );
  590.         if( osErr == noErr && helpMenuHandle != nil )
  591.         {
  592.             char    itemString[ 256 ];
  593.             systemHelpItemCount = CountMItems( helpMenuHandle );
  594.             appendmenu( helpMenuHandle, GetPreloadString( iHoWSampleHelpFileName, itemString ) );
  595.             if( !helpServerPresent )
  596.                 DisableItem( helpMenuHandle, CountMItems( helpMenuHandle ) );
  597.             AppendMenu( helpMenuHandle, "\p(-" );
  598.             appendmenu( helpMenuHandle, GetPreloadString( iDialogHelpString, itemString ) );
  599.             if( !helpServerPresent )
  600.                 DisableItem( helpMenuHandle, CountMItems( helpMenuHandle ) );
  601.             appendmenu( helpMenuHandle, GetPreloadString( iMenuHelpString, itemString ) );
  602.             if( !helpServerPresent )
  603.                 DisableItem( helpMenuHandle, CountMItems( helpMenuHandle ) );
  604.         }
  605.     }
  606.     
  607.     {    /* Install the handler for our Apple events. */
  608.         (void) AEInstallEventHandler( kCoreEventClass, typeWildCard, (EventHandlerProcPtr) AppleEventHandler, 0, false );
  609.     }
  610.     
  611.     {    /* Initialize helpCommandChar. */
  612.         char    string[ 256 ];
  613.         helpCommandChar = *GetPreloadString( iHelpCommandChar, string );
  614.     }
  615.     
  616.     return true;
  617.     
  618. }
  619.  
  620. #pragma    segment    Main
  621. void    Terminate( )
  622. {
  623.  
  624.     /* Deregister with Help on Wheels. */
  625.     (void) HoWDeregister( );
  626.     
  627.     ExitToShell( );
  628.     
  629. }
  630.  
  631. /* user interaction and alerts */
  632.  
  633. #pragma    segment    Main
  634. OSErr    InteractOrTimeOut( short timeOut )
  635. {    /* Use the application's icon family as the notification icon family. */
  636.     OSErr    osErr;
  637.     NMRec    nmRec;
  638.     
  639.     nmRec.qType = nmType;
  640.     nmRec.nmMark = 1;
  641.     (void) GetIconSuite( &nmRec.nmIcon, rPrincipalIconFamily, svAllAvailableD );
  642.     if( nmRec.nmIcon != nil )
  643.         HNoPurge( nmRec.nmIcon );
  644.     nmRec.nmSound = nil;
  645.     nmRec.nmStr = nil;
  646.     nmRec.nmResp = nil;
  647.     
  648.     /* We can call AEInteractWithUser whether or not we are in an Apple event context.  */
  649.     osErr = AEInteractWithUser( timeOut, &nmRec, nil );
  650.     
  651.     if( nmRec.nmIcon != nil )
  652.         HPurge( nmRec.nmIcon );
  653.     (void) DisposeIconSuite( nmRec.nmIcon, true );
  654.     
  655.     return osErr;
  656.     
  657. }
  658.  
  659. #pragma    segment    Main
  660. void    InfoAlert( const char *string )
  661. {
  662.  
  663.     (void) InteractOrTimeOut( kNoTimeOut );
  664.     
  665.     paramtext( string, nil, nil, nil );
  666.     (void) NoteAlert( rGeneralAlert, nil );
  667.         
  668. }
  669.  
  670. #pragma    segment    Main
  671. void    WarningAlert( const char *string )
  672. {
  673.  
  674.     (void) InteractOrTimeOut( kNoTimeOut );
  675.     
  676.     paramtext( string, nil, nil, nil );
  677.     (void) CautionAlert( rGeneralAlert, nil );
  678.         
  679. }
  680.  
  681. #pragma    segment    Main
  682. void    ErrorAlert( const char *string, long code )
  683. {
  684.     char    errorString[ 256 ];
  685.  
  686.     (void) InteractOrTimeOut( kNoTimeOut );
  687.     
  688.     SysBeep( 0 );
  689.     if( code == 0 )
  690.         strcpy( errorString, string );
  691.     else if( code == memFullErr )
  692.         sprintf( errorString, "%s  [out of memory]", string );
  693.     else
  694.         sprintf( errorString, "%s  [%ld]", string, code );
  695.     paramtext( errorString, nil, nil, nil );
  696.     (void) StopAlert( rGeneralAlert, nil );
  697.         
  698. }
  699.  
  700. /* 'STR#' utilities */
  701.  
  702. #pragma    segment    Main
  703. char    *GetMessage( short messageID, char *message )
  704. {
  705.  
  706.     if( messageID <= 0 )
  707.     {
  708.         char    genericErrorMessage[ 256 ];
  709.         getindstring( genericErrorMessage, rMessages, iGenericErrorMessage );
  710.         if( ResError( ) == noErr )
  711.             sprintf( message, genericErrorMessage, messageID );
  712.     }
  713.     else
  714.         getindstring( message, rMessages, messageID );
  715.     
  716.     if( ResError( ) != noErr )
  717.         sprintf( message, "Internal error %d has occurred.", messageID );
  718.     
  719.     return message;
  720.     
  721. }
  722.  
  723. #pragma    segment    Main
  724. void    WarningMessage( short warningID )
  725. {
  726.     char    warningMessage[ 256 ];
  727.         
  728.     WarningAlert( GetMessage( warningID, warningMessage ) );
  729.     
  730. }
  731.  
  732. #pragma    segment    Main
  733. void    ErrorMessage( short errorID, long code )
  734. {
  735.     char    errorMessage[ 256 ];
  736.         
  737.     ErrorAlert( GetMessage( errorID, errorMessage ), code );
  738.     
  739. }
  740.  
  741. #pragma    segment    Main
  742. char    *GetPreloadString( short stringID, char *string )
  743. {
  744.  
  745.     getindstring( string, rPreloadStrings, stringID );
  746.     
  747.     if( ResError( ) != noErr )
  748.     {
  749.         ErrorMessage( iCantGetPreloadString, ResError( ) );
  750.         strcpy( string, "ERROR" );
  751.     }
  752.     
  753.     return string;
  754.     
  755. }
  756.  
  757. /* dos */
  758.  
  759. #pragma    segment    Main
  760. pascal    Boolean    AboutModalDialogFilter( DialogPtr dialog, EventRecord *theEvent, short *itemHit )
  761. {
  762.  
  763.     /* This filter is needed because without it, the about alert doesn't get highlighted. */
  764.     /* We force highlighting on the first event. */
  765.     if( !GetWRefCon( (WindowPtr) dialog ) )
  766.     {
  767.         SetWRefCon( (WindowPtr) dialog, true );
  768.         HiliteWindow( (WindowPtr) dialog, true );
  769.         SetDialogDefaultItem( dialog, ok );
  770.         SetDialogCancelItem( dialog, ok );
  771.         SetDialogTracksCursor( dialog, false );
  772.     }
  773.     
  774.     return GeneralModalDialogFilter( dialog, theEvent, itemHit );
  775.     
  776. }
  777.  
  778. #pragma    segment    Main
  779. void    DoAbout( void )
  780. {
  781.     Boolean    balloonState = helpMgrPresent && HMGetBalloons( );
  782.     
  783.     if( helpMgrPresent )
  784.         HMSetBalloons( !balloonState );
  785.  
  786.     topicTag = rAboutAlert * cTagSpacing;
  787.     (void) Alert( rAboutAlert, (ModalFilterProcPtr) AboutModalDialogFilter );
  788.     
  789.     if( helpMgrPresent )
  790.         HMSetBalloons( balloonState );
  791.     
  792. }
  793.  
  794. #pragma    segment    Main
  795. void    DoMenuCommand( long menuResult, Boolean option )
  796. {
  797. #pragma    unused( option )
  798.     short    menuID = HiWord( menuResult );
  799.     short    menuItem = LoWord( menuResult );
  800.     Str255    daName;
  801.     short    menuTopicTag = menuID * cTagSpacing + menuItem;
  802.     
  803.     switch( menuID )
  804.     {
  805.     case mApple:
  806.         switch( menuItem )
  807.         {
  808.         case iAbout:
  809.             /* This is an example of a casual help display. */
  810.             /* A casual display is effective only if the server is started and running in */
  811.             /* the background, with the "follow" option in effect, and already displaying */
  812.             /* this client's help file. */
  813.             DisplayHelp( menuTopicTag, true );
  814.             DoAbout( );
  815.             break;
  816.         default:
  817.             GetItem( GetMHandle( mApple ), menuItem, daName );
  818.             (void) OpenDeskAcc( daName );
  819.             break;
  820.         }
  821.         break;
  822.     case mFile:
  823.         switch( menuItem )
  824.         {
  825.         case iQuit:
  826.             DisplayHelp( menuTopicTag, true );
  827.             done = true;
  828.             break;
  829.         }
  830.         break;
  831.     case mEdit:
  832.         DisplayHelp( menuTopicTag, true );
  833.         (void) SystemEdit( menuItem - 1 );
  834.         break;
  835.     case mDemo:
  836.         switch( menuItem )
  837.         {
  838.         case iDoNothing:
  839.             DisplayHelp( menuTopicTag, true );
  840.             DoNothingDialog( );
  841.             UnloadSeg( (Ptr) DoNothingDialog );
  842.             break;
  843.         }
  844.         break;
  845.     case kHMHelpMenuID:
  846.         /* This is a non-casual help display.  The user directly asked for help. */
  847.         switch( menuItem - systemHelpItemCount )
  848.         {
  849.         case iHoWSampleHelp:
  850.             DisplayHelp( topicTag, false );
  851.             break;
  852.         case iDialogHelp:
  853.             DisplayHelp( dialogTag, false );
  854.             break;
  855.         case iMenuHelp:
  856.             DisplayHelp( menuTag, false );
  857.             break;
  858.         }
  859.     }
  860.     
  861.     HiliteMenu( 0 );
  862.     
  863. }
  864.  
  865. #pragma    segment    Main
  866. void    DoContentClick( WindowPtr window, EventRecord *event )
  867. {
  868. #pragma    unused( event )
  869.     
  870.     if( IsAppWindow( window ) )
  871.     {
  872.     }
  873.     
  874. }
  875.  
  876. #pragma    segment    Main
  877. void    DoKeyDown( EventRecord *event )
  878. {
  879.     unsigned    char    characterCode = event->message & charCodeMask;
  880.  
  881.     switch( characterCode )
  882.     {
  883.     case 0x05:    /* Help */
  884.         /* This is a non-casual help display.  The user directly asked for help. */
  885.         DisplayHelp( topicTag, false );
  886.         break;
  887.     }
  888.     
  889. }
  890.  
  891. #pragma    segment    Main
  892. void    DoUpdate( WindowPtr window )
  893. {
  894.  
  895.     if( IsAppWindow( window ) )
  896.     {
  897.         BeginUpdate( window );
  898.         /* Do update processing here. */
  899.         EndUpdate( window );
  900.     }
  901.     
  902. }
  903.  
  904. #pragma    segment    Main
  905. void    DoActivate( )
  906. {
  907.     
  908.     /* Set cursor to arrow shape. */
  909.     SetCursor( &qd.arrow );
  910.     
  911. }
  912.  
  913. #pragma    segment    Main
  914. void    DoDeactivate( )
  915. {
  916.     
  917. }
  918.  
  919. /* event handlers */
  920.  
  921. #pragma    segment    Main
  922. void    HandleMouseDownEvent( EventRecord *pEvent )
  923. {
  924.     WindowPtr    window;
  925.     short    part = FindWindow( pEvent->where, &window );
  926.     Boolean    option = pEvent->modifiers & optionKey || false;
  927.     
  928.     switch( part )
  929.     {
  930.     case inMenuBar:
  931.         AdjustMenus( option );
  932.         DoMenuCommand( MenuSelect( pEvent->where ), option );
  933.         break;
  934.     case inSysWindow:
  935.         SystemClick( pEvent, window );
  936.         break;
  937.     case inContent:
  938.         DoContentClick( window, pEvent );
  939.         break;
  940.     }
  941.     
  942. }
  943.  
  944. #pragma    segment    Main
  945. void    HandleKeyDownEvent( EventRecord *pEvent )
  946. {
  947.     char    characterCode = pEvent->message & charCodeMask;
  948.     Boolean    option = pEvent->modifiers & optionKey || false;
  949.     
  950.     if( characterCode == helpCommandChar && pEvent->modifiers & cmdKey )
  951.     {    /* Map Command-<helpCommandChar> to the Help key. */
  952.         pEvent->modifiers &= ~cmdKey;
  953.         pEvent->message &= ~charCodeMask;
  954.         pEvent->message |= 0x05;
  955.     }
  956.     
  957.     if( pEvent->modifiers & cmdKey )
  958.     {
  959.         if( pEvent->what == keyDown )
  960.         {
  961.             AdjustMenus( option );
  962.             DoMenuCommand( MenuKey( characterCode ), option );
  963.         }
  964.     }
  965.     else DoKeyDown( pEvent );
  966.     
  967. }
  968.  
  969. #pragma    segment    Main
  970. void    HandleUpdateEvent( EventRecord *pEvent )
  971. {
  972.  
  973.     DoUpdate( (WindowPtr) pEvent->message );
  974.     
  975. }
  976.  
  977. #pragma    segment    Main
  978. void    HandleActivateEvent( EventRecord *pEvent )
  979. {
  980.     
  981.     if( pEvent->modifiers & activeFlag )
  982.     {
  983.         DoActivate( );
  984.     }
  985.     else
  986.     {
  987.         DoDeactivate( );
  988.     }
  989.     
  990. }
  991.     
  992. #pragma    segment    Main
  993. void    HandleOSEvent( EventRecord *pEvent )
  994. {
  995.     
  996.     switch( pEvent->message & osEvtMessageMask )
  997.     {
  998.     case suspendResumeMessage << 24:
  999.         if( pEvent->message & resumeFlag )
  1000.         {
  1001.             inForeground = true;
  1002.             DoActivate( );
  1003.         }
  1004.         else
  1005.         {
  1006.             inForeground = false;
  1007.             DoDeactivate( );
  1008.         }
  1009.         break;
  1010.     }
  1011.     
  1012. }
  1013.     
  1014. #pragma    segment    Main
  1015. void    HandleHighLevelEvent( EventRecord *pEvent )
  1016. {
  1017.     
  1018.     switch( pEvent->message )
  1019.     {
  1020.     /* Any application-defined high-level event processing goes here. */
  1021.     default:
  1022.         (void) AEProcessAppleEvent( pEvent );
  1023.         UnloadSeg( (Ptr) HoWRegister );        /* in case this was a HoW Apple event */
  1024.         break;
  1025.     }
  1026.     
  1027. }
  1028.  
  1029. /* event loop */
  1030.  
  1031. #pragma    segment    Main
  1032. void    EventLoop( )
  1033. {
  1034.     EventRecord    myEvent;
  1035.     
  1036.     FlushEvents( everyEvent, 0 );
  1037.     
  1038.     while( !done )
  1039.     {
  1040.         topicTag = 0;
  1041.         if( WaitNextEvent( everyEvent, &myEvent, sleepTicks, nil ) )
  1042.         {
  1043.             switch( myEvent.what )
  1044.             {
  1045.             case mouseDown:
  1046.                 HandleMouseDownEvent( &myEvent );
  1047.                 break;
  1048.             case keyDown:
  1049.                 HandleKeyDownEvent( &myEvent );
  1050.                 break;
  1051.             case updateEvt:
  1052.                 HandleUpdateEvent( &myEvent );
  1053.                 break;
  1054.             case osEvt:
  1055.                 HandleOSEvent( &myEvent );
  1056.                 break;
  1057.             case kHighLevelEvent:
  1058.                 HandleHighLevelEvent( &myEvent );
  1059.                 break;
  1060.             }
  1061.         }
  1062.         else
  1063.         {    /* idle */
  1064.         }
  1065.     }
  1066.     
  1067. }
  1068.  
  1069. /* main */
  1070.  
  1071. #pragma    segment    Main
  1072. void    main( )
  1073. {
  1074.     extern    void    _DataInit( );
  1075.     
  1076.     UnloadSeg( (Ptr) _DataInit );    /* note that _DataInit must not be in Main! */
  1077.  
  1078.     MaxApplZone( );                    /* expand the heap so code segments load at the top */
  1079.     
  1080.     MoreMasters( );
  1081.     MoreMasters( );
  1082.  
  1083.     /* Initialize globals, System, and application items. */
  1084.     if( !Initialize( ) ) Terminate( );
  1085.     
  1086.     UnloadSeg( (Ptr) Initialize );    /* note that Initialize must not be in Main! */
  1087.     
  1088.     EventLoop( );
  1089.     
  1090.     Terminate( );
  1091.     
  1092. }